package com.ls.bpmn.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ls.bpmn.dto.*;
import com.ls.bpmn.entity.BpmnProcinst;
import com.ls.bpmn.from.TaskDoneQueryForm;
import com.ls.bpmn.from.TaskQueryForm;
import com.ls.bpmn.from.TaskWorkQueryForm;
import com.ls.bpmn.mapper.BpmnProcessMapper;
import com.ls.bpmn.service.IProcessService;
import com.ls.common.Empty;
import com.ls.common.enums.TaskTypeEnum;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.impl.RepositoryServiceImpl;
import org.camunda.bpm.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.camunda.bpm.engine.impl.pvm.process.ActivityImpl;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.repository.ProcessDefinitionQuery;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.IdentityLink;
import org.camunda.bpm.engine.task.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @Author lisheng
 * @Date 2025/05/07 01:05
 **/
@Service
public class ProcessServiceImpl extends ServiceImpl<BpmnProcessMapper, BpmnProcinst> implements IProcessService {
    private static final Logger log = LoggerFactory.getLogger(ProcessServiceImpl.class);

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Override
    public StartReturnDTO start(ProcessStartDTO dto) throws Exception {
        StartReturnDTO srd = new StartReturnDTO();

        ProcessInstance p = null;
        try {
            String id = dto.getProcessDefinitionId();
            String key = dto.getProcessDefinitionKey();
            String userId = dto.getUserId();

            ProcessDefinition pd = null;

            ProcessDefinitionQuery query = this.repositoryService.createProcessDefinitionQuery();
            if (Empty.isNotEmpty(id)) {
                pd = query.processDefinitionId(id).singleResult();
            } else {
                pd = query.processDefinitionKey(key).active().latestVersion().singleResult();
            }

            if (Empty.isEmpty(pd)) {
                throw new Exception("未找到可用的流程定义！");
            } else {
                identityService.setAuthenticatedUserId(userId);
                p = this.runtimeService.startProcessInstanceById(pd.getId(), dto.getBusinessKey(), dto.getVariables());
                identityService.setAuthenticatedUserId(null);
            }

            String processInstanceId = p.getProcessInstanceId();
            String startTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");

            if (!dto.isDraft()) {
                // 获取第一个环节待办任务
                List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();

                Task t = null;
                if (Empty.isNotEmpty(tasks)) {
                    t = tasks.get(0);

                    // 签收任务
                    if (Empty.isEmpty(t.getAssignee())) {
                        this.taskService.setAssignee(t.getId(), userId);
                    }
                }

                if (Empty.isNotEmpty(t)) {
                    identityService.setAuthenticatedUserId(userId);
                    this.taskService.complete(t.getId());
                    identityService.setAuthenticatedUserId(null);
                }
            }

            srd.setProcessInstanceId(processInstanceId);
            srd.setBusinessKey(dto.getBusinessKey());
            srd.setStartTime(startTime);
            srd.setUserId(userId);
            return srd;
        } catch (Exception e) {
            log.error("流程启动异常！" + e.getMessage(), e);

            // 流程实例创建成功，但是第一个环节待办任务提交失败后删除流程实例
            if (Empty.isNotEmpty(p)) {
                // 运行级别流程实例删除
                this.runtimeService.deleteProcessInstance(p.getProcessInstanceId(), e.getMessage());

                // 历史级别流程实例删除
                this.historyService.deleteHistoricProcessInstance(p.getProcessInstanceId());
            }

            throw new Exception(e.getMessage());
        }
    }

    @Override
    public SubmitReturnDTO submit(ProcessSubmitDTO dto) throws Exception {
        String taskId = dto.getTaskId();
        String userId = dto.getUserId();

        // 任务获取
        Task t = taskService.createTaskQuery().taskId(taskId).singleResult();

        if(Empty.isEmpty(t)){
            throw new Exception("任务不存在！");
        }else{
            // 签收任务
            if(Empty.isEmpty(t.getAssignee())){
                this.taskService.setAssignee(t.getId(), userId);
            }

            identityService.setAuthenticatedUserId(userId);
            this.taskService.complete(t.getId(), dto.getVariables());
            identityService.setAuthenticatedUserId(null);
        }

        SubmitReturnDTO srd = new SubmitReturnDTO();
        srd.setProcessInstanceId(t.getProcessInstanceId());
        srd.setTaskId(taskId);
        srd.setUserId(userId);

        return srd;
    }

    @Override
    public List<HistoricActivityInstanceDTO> findActivityInstanceDTO(String processInstanceId, String parentActInstId) {
        List<HistoricActivityInstanceDTO> list = this.baseMapper.selectHistoricActivityInstanceDTO(processInstanceId, parentActInstId);

        // 流程定义实体类
        ProcessDefinitionEntity pde = null;
        if(Empty.isNotEmpty(list)){
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionKey(list.get(0).getProcessDefinitionKey())
                    .latestVersion()
                    .singleResult();

            pde = (ProcessDefinitionEntity)((RepositoryServiceImpl) repositoryService)
                    .getDeployedProcessDefinition(pd.getId());
        }

        for(HistoricActivityInstanceDTO a : list){
            // 环节ID
            String nodeId = a.getKey();
            if("multiInstanceBody".equals(a.getType())) {
                nodeId = a.getKey().substring(0, a.getKey().indexOf("#"));
            } else if("subProcess".equals(a.getType())){

            }else if("userTask".equals(a.getType())){
                // 用户任务环节时数据封装

                // 处理人姓名聚合
//                    UserDTO u = this.userService.getUser(a.getAssignee());
//                    if(Empty.isNotEmpty(u)){
//                        a.setAssigneeName(Empty.isEmpty(u.getOrgName())? u.getUserName() : u.getOrgName() + "-" + u.getUserName());
//                    }

                // 待办任务没有被签收设置任务候选人
                if(Empty.isEmpty(a.getAssignee()) && Empty.isEmpty(a.getEndTime())) {
                    Set<String> candidates = new HashSet<>();
                    List<IdentityLink> ilist = this.taskService.getIdentityLinksForTask(a.getTaskId());
                    for (IdentityLink i : ilist) {
//                            UserDTO c = this.userService.getUser(i.getUserId());
//                            if (Empty.isNotEmpty(c) && Empty.isNotEmpty(c.getUserName())) {
//                                candidates.add(Empty.isEmpty(c.getOrgName()) ? c.getUserName() : c.getOrgName() + "-" + c.getUserName());
//                            }
                    }

                    a.setCandidate(StringUtils.join(candidates,","));
                }
            }

            a.setTypeName(TaskTypeEnum.fromCode(a.getType()).getName());

            if(!"startEvent,noneEndEvent".contains(a.getType()) && Empty.isNotEmpty(pde)){
                // 环节对象
                ActivityImpl node = pde.findActivity(nodeId);

                // 流程环节名称设置
                if(Empty.isNotEmpty(node)) {
                    a.setName(node.getName());
                }
            }
        }

        return list;
    }

    @Override
    public Page<TaskListDTO> findWorkOrder(TaskWorkQueryForm form) {
        Page<TaskListDTO> page = new Page<>(form.getPageIndex(), form.getPageSize());

        List<String> groupIds = form.getGroups();

        String nodeName = null;
        if(Empty.isNotEmpty(form.getNodeName())){
            nodeName = "%" + nodeName + "%";
        }

        List<TaskListDTO> list = this.baseMapper.selectWorkOrder(
                page,
                form.getUserId(),
                form.getProcessDefinitionId(),
                form.getKeys(),
                form.getBusinessKey(),
                form.getHandle(),
                nodeName,
                form.getCreateTimeStart(),
                form.getCreateTimeEnd(),
                form.getState(),
                groupIds,
                form.getConditions());

        Set<String> pids = new HashSet<>();
        for(TaskListDTO w : list){
            pids.add(w.getProcessInstanceId());
        }

        // 活动信息获取
        Map<String, ActivityInstanceDTO> alist = this.baseMapper.selectActivityInstanceDTO(new ArrayList<>(pids));

        for(TaskListDTO w : list){
            ActivityInstanceDTO a = alist.get(w.getProcessInstanceId());

            if(Empty.isEmpty(w.getEndTime())){
                if(Empty.isNotEmpty(a)){
                    // 当前处理环节名设置
                    if(Empty.isNotEmpty(a.getNames())){
                        w.setCurrentNodeName(a.getNames());
                    }

                    // 当前处理环节ID设置
                    if(Empty.isNotEmpty(a.getNodes())){
                        w.setCurrentNodeId(a.getNodes());
                    }

                    // 当前处理人设置
                    if(Empty.isNotEmpty(a.getAssignees())){
                        w.setAssignee(a.getAssignees());
                    }

                    // 当前候选人设置
                    if(Empty.isNotEmpty(a.getCandidates())){
                        w.setCandidate(a.getCandidates());
                    }
                }

                if("SUSPENDED".equals(w.getState())){
                    w.setState("已挂起");
                }else {
                    w.setState("未办结");
                }
            }else{
                w.setCurrentNodeName("已办结");
                w.setState("已办结");
            }


        }

        return page.setRecords(list);    }

    @Override
    public Page<TaskDTO> todo(TaskQueryForm form) {
        Page<TaskDTO> page = new Page<TaskDTO>(form.getPageIndex(), form.getPageSize());

        List<String> groupIds = form.getGroups();

        String taskName = null;
        if(Empty.isNotEmpty(form.getTaskName())){
            taskName = "%" + taskName + "%";
        }

        return page.setRecords(
                this.baseMapper.selectTodoTask(
                        page,
                        form.getUserId(),
                        form.getProcessDefinitionId(),
                        form.getKeys(),
                        form.getBusinessKey(),
                        taskName,
                        form.getAssigned(),
                        groupIds,
                        form.getConditions(),
                        form.getCreateTimeStart(),
                        form.getCreateTimeEnd(),
                        form.getOrderBy(),
                        form.getSort()
                ));
    }

    @Override
    public Page<TaskDTO> done(TaskDoneQueryForm form) {
        Page<TaskDTO> page = new Page<TaskDTO>(form.getPageIndex(), form.getPageSize());

        String taskName = null;
        if(Empty.isNotEmpty(form.getTaskName())){
            taskName = "%" + form.getTaskName() + "%";
        }

        return page.setRecords(
                this.baseMapper.selectDoneTask(
                        page,
                        form.getUserId(),
                        form.getProcessDefinitionId(),
                        form.getKeys(),
                        form.getBusinessKey(),
                        taskName,
                        form.getConditions(),
                        form.getCreateTimeStart(),
                        form.getCreateTimeEnd(),
                        form.getOrderBy(),
                        form.getSort()
                ));
    }
}